//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using ManagementStudioServices.Interfaces;
using Microsoft.Research.ScientificWorkflow.ManagementStudioControls.Common;
using Microsoft.Research.ScientificWorkflow.ManagementStudioModels;
using Microsoft.Research.ScientificWorkflow.TridentUtilities;
using Microsoft.Research.ScientificWorkflow.UIDesigner.WpfComposite;
using Microsoft.Practices.Unity;
using SR = Microsoft.Research.DataLayer;
using System;

namespace Microsoft.Research.ScientificWorkflow.ManagementStudioControls.WorkflowManagerModule
{
    public class CompletedJobsListViewPresenter : IListPresenter, INotifyPropertyChanged
    {
        #region Private variables
        /// <summary>
        /// The unity container instance.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// The workflow manager service instance.
        /// </summary>
        private IWorkflowManagerService workflowManagerService;

        /// <summary>
        /// The search view for the available workflows filter.
        /// </summary>
        private UIElement searchView;

        /// <summary>
        /// The Title on the tableview.
        /// </summary>
        private string listViewTitle;

        /// <summary>
        /// The view which displays the workflows.
        /// </summary>
        private UIElement completedWorkflowsListView;

        /// <summary>
        /// The list of context menu items.
        /// </summary>
        private Collection<MenuItem> contextMenuItems;

        /// <summary>
        /// The selected index of the listview.
        /// </summary>
        private int selectedIndex;

        /// <summary>
        /// Flag to check if items were retreived in the query. 
        /// </summary>
        private bool emptyList;

        /// <summary>
        /// Holds the current filter.
        /// </summary>
        private string currentFilter;

        /// <summary>
        /// The items to be displayed on the tableview.
        /// </summary>
        private ObservableCollection<object> items;
        static string selectAll = ManagementStudioResourceManager.GetString("SelectAllItems");
        string jobName = string.Empty;
        string jobStatus = selectAll;
        string nodeName = selectAll;

        Collection<string> jobNodeList = new Collection<string>();
        Collection<string> statusList = new Collection<string>();



        #endregion


        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="container">The unity contaier</param>
        /// <param name="view">The center pane view of the presenter.</param>
        /// <param name="service">The workflow manager service</param>
        public CompletedJobsListViewPresenter(IUnityContainer container, CompletedJobsListView view, IWorkflowManagerService service)
        {
            this.container = container;
            this.completedWorkflowsListView = view;
            this.workflowManagerService = service;

            this.items = new ObservableCollection<object>();
            this.ListViewTitle = ManagementStudioResourceManager.GetString("TableTitleCompletedWorkflows");
            this.searchView = this.container.Resolve<CompletedJobsSearchView>();
            (this.searchView as CompletedJobsSearchView).DataContext = this;

            (this.completedWorkflowsListView as CompletedJobsListView).DataContext = this;
            this.Items.CollectionChanged += Items_CollectionChanged;

            // Initialize the commands.
            this.DeleteJobCommand = new DelegateCommand<JobViewModel>(this.ExecuteDeleteJob, this.JobCommandsCanExecute);
            this.ShowProvenanceCommand = new DelegateCommand<JobViewModel>(this.ExecuteShowProvenance, this.JobCommandsCanExecute);
            this.RefreshListCommand = new DelegateCommand<object>(p => { this.DisplayItems(this.currentFilter); });
            this.SearchCommand = new DelegateCommand<object>(p => this.SearchCommandExecute());

            this.SetContextMenu();

            jobNodeList.Insert(0, selectAll);
            //Populate the job status
            statusList.Add(selectAll);
            statusList.Add(ManagementStudioResourceManager.GetString("JobStatusAborted"));
            statusList.Add(ManagementStudioResourceManager.GetString("JobStatusCompleted"));

            Collection<SR.Machine> nodeList = workflowManagerService.GetAllMachines(null);
            foreach (SR.Machine node in nodeList)
            {
                if (!jobNodeList.Contains(node.Name.ToUpper()))
                {
                    jobNodeList.Add(node.Name.ToUpper());
                }
            }

        }


        #region Public Properties
        /// <summary>
        /// The command executed when the search button is clicked.
        /// </summary>
        public DelegateCommand<object> SearchCommand { get; private set; }

        /// <summary>
        /// The command executed when the user deletes a job.
        /// </summary>
        public DelegateCommand<JobViewModel> DeleteJobCommand { get; private set; }

        /// <summary>
        /// The command executed to show the provenance information.
        /// </summary>
        public DelegateCommand<JobViewModel> ShowProvenanceCommand { get; private set; }

        /// <summary>
        /// The command to refresh the list.
        /// </summary>
        public DelegateCommand<object> RefreshListCommand { get; private set; }

        /// <summary>
        /// Property to check if items were retreived in the query.
        /// </summary>
        public bool EmptyList
        {
            get
            {
                return this.emptyList;
            }
            private set
            {
                this.emptyList = value;
                this.OnNotifyPropertyChanged("EmptyList");
            }
        }

        #endregion

        #region IListPresenter Members

        /// <summary>
        /// The items to be displayed in the table view.
        /// </summary>
        public ObservableCollection<object> Items
        {
            get
            {
                return this.items;
            }
        }

        /// <summary>
        /// The title of the table view.
        /// </summary>
        public string ListViewTitle
        {
            get
            {
                return this.listViewTitle;
            }
            set
            {
                this.listViewTitle = value;
                this.OnNotifyPropertyChanged("ListViewTitle");
            }
        }

        /// <summary>
        /// Display the items in the table view.
        /// </summary>
        /// <param name="queryString"></param>
        public void DisplayItems(string queryString)
        {
            try
            {
                Items.Clear();

                SR.Job.ISearch.ISearchClause clause = null;
                if (!string.IsNullOrEmpty(queryString))
                {
                    this.currentFilter = queryString;
                    if (queryString.Equals(ManagementStudioControlsConstants.RECENTLYCOMPLETEDJOBS, StringComparison.OrdinalIgnoreCase))
                    {
                        DateTime cutoffDate = DateTime.Today.AddDays(-5);
                        clause = SR.Job.ISearch.CreatedDate(SR.DateTimeField.Condition.AfterOrEqual, cutoffDate);
                    }
                    else if (queryString.Equals(ManagementStudioControlsConstants.JOBSCOMPLETEDBYMEFILTERNAME, StringComparison.OrdinalIgnoreCase))
                    {
                        clause = SR.Job.ISearch.ScheduledBy(SR.ObjectFieldBase.Condition.Equals, TridentAuthentication.LoggedInUser);
                    }
                }

                // Get the required items.
                Collection<SR.Job> jobList = this.workflowManagerService.GetCompletedJobs(clause);

                // Set the selected Index. Show the empty message if no items found.
                if (jobList != null && jobList.Count > 0)
                {
                    this.EmptyList = false;

                    // Set status.
                    this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusJobsCount",
                         jobList.Count.ToString(CultureInfo.CurrentUICulture)));

                    // Add the items to the list.
                    foreach (SR.Job job in jobList)
                    {
                        try
                        {
                            JobViewModel jobViewmodel = new JobViewModel(job);
                            Items.Add(jobViewmodel);
                        }
                        catch (System.Security.SecurityException)
                        {
                            // Ignore: The user might not have permissions on the workflow.
                        }
                    }
                    this.SelectedIndex = 0;
                }
                else
                {
                    // Set the empty list property to true. Shows the empty list message and disables the buttons.
                    this.EmptyList = true;
                    this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);
                }

                this.RaiseCanExecuteEvents();
            }
            catch (TridentCustomException ex)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
            }
        }


        /// <summary>
        /// The search view for the available workflows filter.
        /// </summary>
        public UIElement SearchView
        {
            get
            {
                return this.searchView;
            }
        }

        /// <summary>
        /// The view which displays the workflows.
        /// </summary>
        public UIElement ListView
        {
            get
            {
                return this.completedWorkflowsListView;
            }
        }

        /// <summary>
        /// The menu items for the listview items.
        /// </summary>
        public Collection<MenuItem> MenuItemCollection
        {
            get
            {
                return this.contextMenuItems;
            }
        }

        /// <summary>
        /// The selected index of the list.
        /// </summary>
        public int SelectedIndex
        {
            get
            {
                return this.selectedIndex;
            }
            set
            {
                this.selectedIndex = value;
                this.OnNotifyPropertyChanged("SelectedIndex");
            }
        }

        /// <summary>
        /// Job name for search
        /// </summary>
        public string JobName
        {
            get
            {
                return jobName;
            }
            set
            {
                jobName = value;
                this.OnNotifyPropertyChanged("JobName");
            }
        }


        /// <summary>
        /// Selected Job status for search
        /// </summary>
        public string JobStatus
        {
            get
            {
                return jobStatus;
            }
            set
            {
                if (null == value)
                {
                    jobStatus = selectAll;
                }
                else
                {
                    jobStatus = value;
                }
                this.OnNotifyPropertyChanged("JobStatus");
            }
        }


        /// <summary>
        /// Selected node name for search
        /// </summary>
        public string NodeName
        {
            get
            {
                return nodeName;
            }
            set
            {
                if (null == value)
                {
                    nodeName = selectAll;
                }
                else
                {
                    nodeName = value;
                }
                this.OnNotifyPropertyChanged("NodeName");
            }
        }

        /// <summary>
        /// Job Status list for search
        /// </summary>
        public Collection<string> StatusList
        {
            get
            {
                return statusList;
            }
            set
            {
                statusList = value;
            }
        }


        /// <summary>
        /// Job node list for search
        /// </summary>
        public Collection<string> JobNodeList
        {
            get
            {
                return jobNodeList;
            }
            set
            {
                jobNodeList = value;
            }
        }
        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// The property changed event. Fired when a proprty value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Fires the property changed event.
        /// </summary>
        /// <param name="propertyName">Name of the property which has been modified</param>
        private void OnNotifyPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

        #region Private methods


        /// <summary>
        /// implementation for the search click button.
        /// </summary>
        private void SearchCommandExecute()
        {
            try
            {
                Items.Clear();

                // This is the place wNodeNamehere the workflows are retrieved from the registry. The search call ideally goes here.
                // Get the required items.
                SR.Job.ISearch.ISearchClause filter = null;
                List<SR.Job.ISearch.ISearchClause> filterArray = new List<SR.Job.ISearch.ISearchClause>();
                if (!string.IsNullOrEmpty(this.JobName))
                {
                    filterArray.Add(SR.Job.ISearch.Name(SR.StringField.Condition.Contains, this.JobName));
                }

                if (!string.IsNullOrEmpty(this.JobStatus) && this.JobStatus != selectAll)
                {
                    SR.JobStatus status = (SR.JobStatus)Enum.Parse(typeof(SR.JobStatus), this.JobStatus);
                    filterArray.Add(SR.Job.ISearch.Status(Microsoft.Research.DataLayer.EnumFieldBase.Condition.Equals, status));
                }

                if (!string.IsNullOrEmpty(this.NodeName) && this.NodeName != selectAll)
                {
                    Collection<SR.Machine> nodes = workflowManagerService.GetAllMachinesWithName(this.NodeName);
                    SR.Job.ISearch.ISearchClause machineClause = null;
                    foreach (SR.Machine node in nodes)
                    {
                        if (null == machineClause)
                        {
                            machineClause = SR.Job.ISearch.Machine(Microsoft.Research.DataLayer.RelationFieldBase.Condition.Equals, node);
                        }
                        else
                        {
                            machineClause = SR.Job.ISearch.OR(machineClause, SR.Job.ISearch.Machine(Microsoft.Research.DataLayer.RelationFieldBase.Condition.Equals, node));
                        }
                    }
                    filterArray.Add(machineClause);
                }

                if (filterArray.Count > 1)
                {
                    filter = SR.Job.ISearch.AND(filterArray.ToArray());
                }
                else
                {
                    filter = filterArray.Count > 0 ? filterArray[0] : null;
                }

                Collection<SR.Job> jobList = workflowManagerService.GetCompletedJobs(filter);

                // Set the selected Index. Show the empty message if no items found.
                if (jobList != null && jobList.Count > 0)
                {
                    this.SelectedIndex = 0;
                    this.EmptyList = false;
                    // Set status.
                    this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusWorkflowCount",
                        jobList.Count.ToString(CultureInfo.CurrentUICulture)));

                    // Add the items to the list.
                    // As the Items property is a observable collection and is bound to the listview. Any changes here will be shown in the listview. 
                    foreach (SR.Job job in jobList)
                    {
                        JobViewModel jobViewmodel = new JobViewModel(job);
                        Items.Add(jobViewmodel);
                    }
                }
                else
                {
                    // Set the empty list property to true. Shows the empty list message and disables the buttons.
                    this.EmptyList = true;
                    this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);
                }
                this.RaiseCanExecuteEvents();
            }
            catch (SR.RecordNotFoundException)
            {
                // Set status.
                this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusWorkflowCount",
                    0.ToString(CultureInfo.CurrentUICulture)));
            }
            catch (TridentCustomException ex)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
            }
        }

        /// <summary>
        /// Create the context menu items for the listview items.
        /// </summary>
        private void SetContextMenu()
        {
            this.contextMenuItems = new Collection<MenuItem>();

            MenuItem provenanceMenuItem = new MenuItem();
            provenanceMenuItem.Header = ManagementStudioResourceManager.GetString("MenuItemHeaderShowProvenance");
            provenanceMenuItem.Command = ShowProvenanceCommand;
            provenanceMenuItem.Icon = IconUtilities.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ManagementStudio/provenanceMenuIcon.png");
            this.contextMenuItems.Add(provenanceMenuItem);

            MenuItem deleteMenuItem = new MenuItem();
            deleteMenuItem.Header = ManagementStudioResourceManager.GetString("MenuItemHeaderDeleteJob");
            deleteMenuItem.Command = DeleteJobCommand;
            deleteMenuItem.Icon = IconUtilities.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ManagementStudio/delete.png");
            this.contextMenuItems.Add(deleteMenuItem);
        }

        /// <summary>
        /// Delete the Job Selected.
        /// </summary>
        /// <param name="selectedJob">The job to be deleted.</param>
        private void ExecuteDeleteJob(JobViewModel selectedJob)
        {
            try
            {
                if (selectedJob != null)
                {
                    // Get a confirmation from the user.
                    MessageBoxResult result = TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(
                        ManagementStudioResourceManager.GetString("DeleteJobConfirmation"));

                    if (result == MessageBoxResult.Yes)
                    {
                        this.workflowManagerService.DeleteJob(selectedJob.JobId);
                        this.Items.Remove(selectedJob);
                    }
                }
            }
            catch (TridentCustomException ex)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
            }
        }

        /// <summary>
        /// Show provenance information for the job selected.
        /// </summary>
        /// <param name="selectedJob">The job for which the provenance information is to be shown.</param>
        private void ExecuteShowProvenance(JobViewModel selectedJob)
        {
            try
            {
                if (selectedJob != null)
                {
                    switch (selectedJob.MachineScheduled.MachineType)
                    {
                        case SR.MachineType.Virtual:
                            // Show message indicating that the provenance data is not stored as 
                            //  the machine is virtual and no blackboard service is enabled for execution.
                            TridentMessageBox.ShowTridentInformationMessageBox(
                                ManagementStudioResourceManager.GetString("ProvenanceDataNotAvailable"));
                            break;
                        case SR.MachineType.HPCCluster:
                        case SR.MachineType.Desktop:
                        case SR.MachineType.HPCComputeNode:
                        default:
                            this.workflowManagerService.OpenProvenanceWindow(selectedJob.JobId);
                            break;
                    }
                }
            }
            catch (TridentCustomException ex)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
            }
        }

        /// <summary>
        /// Checks if operations can be performed on the job. It enables and disables the buttons and menuitems.
        /// </summary>
        /// <param name="selectedJob">The job selected.</param>
        /// <returns>True if the operations can be performed on the job. False otherwise.</returns>
        private bool JobCommandsCanExecute(JobViewModel selectedJob)
        {
            if (this.EmptyList)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Raise the can execute events so that the UI elements can be enabled/disabled.
        /// </summary>
        private void RaiseCanExecuteEvents()
        {
            this.DeleteJobCommand.RaiseCanExecuteChanged();
            this.ShowProvenanceCommand.RaiseCanExecuteChanged();
        }

        /// <summary>
        /// Event handler for the collection changed event on the Items collection.
        /// </summary>
        /// <param name="sender">The source.</param>
        /// <param name="e">Event arguments</param>
        void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            ICollection<object> collection = sender as ICollection<object>;

            // If collection is empty then disable the buttons.
            this.EmptyList = (collection.Count == 0);
            this.RaiseCanExecuteEvents();
        }

        #endregion
    }
}
